//
//  NSInvocationAdditions.m
//  HigherOrderMessaging
//
//  Created by Ofri Wolfus on 24/09/05.
//  Copyright 2005 Ofri Wolfus. All rights reserved.
//

#import "NSInvocationAdditions.h"
#import "HOMObjCValue-Private.h"
#import "HOMUtilities.h"


@implementation NSInvocation (HOMAdditions)

//Get our return value
- (id)returnValue {
	const char *t = [[self methodSignature] methodReturnType];
	id	r = nil;
	
	if (*t == *@encode(id) || *t == *@encode(Class))
		[self getReturnValue:&r];
	
	return r;
}

/*
 * The implementation of this method is not really efficient
 * as an invocation already holds all the arguments and their sizes,
 * but since digging into NSInvocation's implementation details
 * is not fun, I'm leaving it as is until someone complains.
 */
- (Message *)message {
	id r, m;
	
	// Store the current return value
	[self getReturnValue:&r];
	// Message the MessageBuilder class which in turn
	// returns us a Message instance
	[self invokeWithTarget:objc_getClass("MessageBuilder")];
	// Get the returned message
	[self getReturnValue:&m];
	// Set back the old return value
	[self setReturnValue:&r];
	// And return our message
	return m;
}

@end

//Default is the slow but supported way
BOOL HOMFastInvocationCopy = NO;

@implementation NSInvocation (HOMCopying)

//Force our invocation to copy
- (id)copyWithZone:(NSZone *)zone {
/*#ifdef HOMALLOW_FAST_INVOCATION_COPY
	if (HOMFastInvocationCopy) {
		NSInvocation *newInvocation = (NSInvocation *)NSCopyObject(self, 0, zone);
		
		//Copy the argument frame of the invocation
		unsigned size = [signature frameLength];
		typeof(argumentFrame) newArgumentFrame = malloc(size);
		newInvocation->argumentFrame = memcpy(newArgumentFrame, argumentFrame, size);
		
		//Copy the container
		//newInvocation->container = [container copyWithZone:zone];
		if (container)
			newInvocation->container = [[NSArray allocWithZone:zone] initWithArray:container copyItems:YES];
		
		//An NSInvocation instance retains its signature on initialization and therefor we have to do it manually here
		[[newInvocation methodSignature] retain];
		
		return newInvocation;
	}
#endif*/
	
	NSMethodSignature	*sig = [self methodSignature];
	NSInvocation		*result = [NSInvocation invocationWithMethodSignature:sig];
	
	//Set the selector of the new invocation
	[result setSelector:[self selector]];
	
	//Set the arguments of the new invocation
	unsigned numberOfArgs = [sig numberOfArguments];
	
	for(int i = 0; i < numberOfArgs; i++) {
		void *arg = malloc(sizeOfType([sig getArgumentTypeAtIndex:i]));
		
		[self getArgument:arg atIndex:i];
		[result setArgument:arg atIndex:i];
		free(arg);
	}
	
	//Retain the arguments if we retained them
	if ([self argumentsRetained])
		[result retainArguments];
	
	//We expect that an object returned from -copyWithZone: will not be autoreleased so we must retain our new invocation
	[result retain];
	
	return result;
}

@end
